Some time around 2011-2014, I wrote a script to recursively delete empty folders (if you visited the old link from some forum or blog, you have now been redirected to this updated article). I feel obligated to provide the visitors with a better alternative since the old, dead link still gets some poor visitors.
My solution is loosely based on Kirk Munroe's excellent answer on Stack Overflow. He explains that Get-ChildItem performs head recursion, while what we need is tail recursion.
Quote: "Since you want to remove empty folders, and also remove their parent if they are empty after you remove the empty folders, you need to use tail recursion instead, which processes the folders from the deepest child up to the root. By using tail recursion, there will be no need for repeated calls to the code that removes the empty folders -- one call will do it all for you."
It also handles hidden files and directories. A directory containing a hidden file will not be deleted, while an empty hidden directory will be deleted.
This code is compatible with PowerShell version 2, and there are also a few minor tweaks (PSv2 compatibility = not using the -Directory parameter for Get-ChildItem).
Commented and mostly self-explanatory. For syntax highlighting or downloading a script file, visit GitHub here.
function Remove-EmptyFolders { <# .SYNOPSIS Remove empty folders recursively from a root directory. The root directory itself is not removed. Author: Joakim Borger Svendsen, Svendsen Tech, Copyright 2022. MIT License. .EXAMPLE . .\Remove-EmptyFolders.ps1 Remove-EmptyFolders -Path E:\FileShareFolder .EXAMPLE Remove-EmptyFolders -Path \\server\share\data NB. You might have to change $ChildDirectory.FullName to $ChildDirectory.ProviderPath in the code for this to work. Untested with UNC paths as of 2022-01-28. #> [CmdletBinding()] Param( [String] $Path ) Begin { [Int32] $Script:Counter = 0 if (++$Counter -eq 1) { $RootPath = $Path Write-Verbose -Message "Saved root path as '$RootPath'." } # Avoid overflow. Overly cautious? ~2.15 million directories... if ($Counter -eq [Int32]::MaxValue) { $Counter = 1 } } Process { # List directories. foreach ($ChildDirectory in Get-ChildItem -LiteralPath $Path -Force | Where-Object {$_.PSIsContainer}) { # Use .ProviderPath on Windows instead of .FullName, # in order to support UNC paths (untested). # Process each child directory recursively. Remove-EmptyFolders -Path $ChildDirectory.FullName } $CurrentChildren = Get-ChildItem -LiteralPath $Path -Force # If it's empty, the condition below evaluates to true. Get-ChildItem # returns $null for empty folders. if ($null -eq $CurrentChildren) { # Do not delete the root folder itself. if ($Path -ne $RootPath) { Write-Verbose -Message "Removing empty folder '$Path'." Remove-Item -LiteralPath $Path -Force } } } }
We see that it removes all the folders except ./testdeldir/a/d, which contains a file. The recursion works, and nested empty directories are deleted up to the root path (but not including the root path).
PS /tmp> New-Item -ItemType Directory -Path ./testdeldir/a/x/g/d | Out-Null PS /tmp> New-Item -ItemType Directory -Path ./testdeldir/a/b/c/e | Out-Null PS /tmp> New-Item -ItemType Directory -Path ./testdeldir/a/c | Out-Null PS /tmp> New-Item -ItemType Directory -Path ./testdeldir/a/d/f | Out-Null PS /tmp> 'testfile' | Set-Content ./testdeldir/a/d/file.txt PS /tmp> . ./Remove-EmptyFolders.ps1 # dot-source to load the function PS /tmp> Remove-EmptyFolders -Path ./testdeldir/ -Verbose VERBOSE: Saved root path as './testdeldir/'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/b/c/e'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/b/c'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/b'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/c'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/d/f'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/x/g/d'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/x/g'. VERBOSE: Removing empty folder '/tmp/testdeldir/a/x'. PS /tmp> gci -rec ./testdeldir/ Directory: /tmp/testdeldir UnixMode User Group LastWriteTime Size Name -------- ---- ----- ------------- ---- ---- drwxrwxr-x joakim joakim 1/28/2022 23:14 4096 a Directory: /tmp/testdeldir/a UnixMode User Group LastWriteTime Size Name -------- ---- ----- ------------- ---- ---- drwxrwxr-x joakim joakim 1/28/2022 23:14 4096 d Directory: /tmp/testdeldir/a/d UnixMode User Group LastWriteTime Size Name -------- ---- ----- ------------- ---- ---- -rw-rw-r-- joakim joakim 1/28/2022 23:14 9 file.txt
As mentioned on Stack Overflow, in this ingenius approach, the simple piece of code below also works. It sorts descending, ensuring children are always processed before parents. This makes it delete recursively when it tests the, depth-first, directories for children and deleted if the count of items inside is 0. Hidden files and folders are handled, by using the -Force parameter for Get-ChildItem.
Get-ChildItem -LiteralPath $FolderToProcess -Recurse -Force -Directory | Sort-Object -Property FullName -Descending | Where-Object { ($_ | Get-ChildItem -Force | Select-Object -First 1).Count -eq 0 } | Remove-Item -Verbose
To make this compatible with PowerShell version 2, you can use this:
Get-ChildItem -LiteralPath $FolderToProcess -Recurse -Force | Where-Object {$_.PSIsContainer} Sort-Object -Property FullName -Descending | Where-Object {-not ($_ | Get-ChildItem -Force | Select-Object -First 1)} | Remove-Item -VerbosePowershell Windows Recursion Function Programming All Categories
Minimum cookies is the standard setting. This website uses Google Analytics and Google Ads, and these products may set cookies. By continuing to use this website, you accept this.